import logging
import cgi

from datastore.resource import *
from datastore.uri import *
from runtime.service import *

class Post(Service):
    """
    A Simple service to process a HTTP Post request and the associated 
    form HTML.
    
    See http://127.0.0.1:8900/examples/post/test-form
    
    This example uses the following spaces resources.
    
    URL mapping data to map an external URL to an internal request queue at 
    a particular Uri.
        internal:base/http/url_mapping/base/examples/post_url_mapping
        (type term:base/services/http/url_mapping)
        (config option res.options.request_queue)
        
    A post form template (the HTML for the form).
        internal:base/http/template/base/examples/post_form 
        (type term:base/services/http/template)
        (config option res.options.post_form)
    
    A definition resource for our post data. This resource specifies the 
    template (internal:base/http/template/base/examples/post_form) that should
    be used to display a resource of this type.
        term:base/examples/post/data
        (type term:base/runtime/definition)
        (autoloaded as part of the post.xml configuration)
        
    A definition resource for the history data.
        term:base/examples/post/history
        (type term:base/runtime/definition)
        (autoloaded as part of the post.xml configuration)
        
    History data. This fetched directly from the datastore and updated 
    immediately. It is not saved in spaces.
        (type term:base/examples/post/history)
        
    In the case of a program generating output, such as for a SOAP request and 
    response, some of the parts of this example are not needed.
    
    The 'post_response' would use a resource of type 
    'term:base/services/http/response'. This means that:
        post_form.xml would not be required.
        the definition 'term:base/examples/post/data' would not be required.
    """

    def cb_start(self, uri, action, res):
        self.logger.log(logging.INFO, "HTTP Post Example (%s)",
                        res.options.request_queue)
                
        # And now a 'process' to handle the incoming post requests to the
        # URL http://127.0.0.1:8900/examples/post/test/
        #self.logger.log(logging.INFO, "zzz %s", res.options)
        self.spaces.set_process(res.options.request_queue,
            self.cb_action)
        
        # Add this UI to the examples index page
        index_res = Resource(type='term:base/services/index/entry')
        index_res.dc_title("HTTP Post")
        index_res.index = Uri('external:examples')
        index_res.uri = res.options.url
        self.spaces.put(Uri('internal:base/index/entries/'), index_res)

                
    def cb_action(self, uri, action, res):
        # We are expecting a resource of type term:base/services/http/post
        # containing some post data.
        self.logger.log(logging.DEBUG, "%s (%s)", action, uri)  
              
        if action == 'exists': 
            return True
        elif action == 'get':
            # Fetch existing data
            sample_data = self.fetch_data()
            return self.init_form(sample_data.form_text,
                                  "Initial form",
                                  """ This is the initial form page""")
        elif action in ['put', 'update']:
            # Make sure we don't process the response (which we generate below).
            if res.get_type() != Uri('term:base/services/http/post'):
                return None
            
            # IMPORTANT NOTE: We must clean our data so as not to introduce 
            # XSS and other security bugs! We only modify the tag characters 
            # so we can can support unicode characters in our data.
            form_data = {}
            for key in res.form_fields.keys():
                form_data[key] = str(res.form_fields[key])
                form_data[key] = form_data[key].replace("<", "&lt;")
                form_data[key] = form_data[key].replace(">", "&gt;")
            del form_data['submit']

            self.logger.log(logging.INFO, "Received a post uri (type %s) %s",
                    str(res.get_type()), str(form_data))
    
            # Just for the hell of it, we will save a copy of the request 
            # in spaces. This is not required, but useful for testing.
            #
            # We must specify spaces=True, otherwise, this callback will just
            # be invoked again (and again...).
            self.spaces.put(uri, res, spaces=True)
            
            # Our response will go to the request uri + 'response'
            post_response_uri = uri + 'response'
            
            # Do the actual work (start by fetching our data)
            sample_data = self.fetch_data()
            
            # Get a timestamp
            timestamp_res = self.spaces.get(
                    Uri('internal:base/services/time_service/timestamp'))
            
            # Add it and the form text to the resource
            name = form_data['name']
            del form_data['name']
            sample_data.form_text.append((timestamp_res.timestamp, name, form_data))
            
            # save it back to the datastore
            self.datastore.put(sample_data)
            
            post_response = self.init_form(sample_data.form_text,
              "Updated form",
              """ This is the form page after a submission has been made.""")

            self.spaces.put(post_response_uri, post_response)
            return post_response_uri
        
    def fetch_data(self):
        # Get sample data resource from the datastore
        # We get it twice - doesn't matter for this example
        try:
            resources = self.datastore.find_type(
                            Uri('term:base/examples/post/history'))
            sample_data = resources[0]
        except NotFoundException:
            sample_data = Resource(
                            type='term:base/examples/post/history')
            sample_data.form_text = []
        return sample_data

    def init_form(self, history, title, description):
        # Publish our default test form (with the correct type)
        res = Resource(type='term:base/examples/post/data')
        res.dc_title(title)
        res.dc_description(description)
        res.history = history
        res.current = {'k1':'', 'k2':'', 'k3':'', 'k4':'', 'k5':''}
        res.name = ''
        res.select_name = 'aaa'
        res.select_value1 = 'one'
        res.select_value2 = 'two'
        res.select_value3 = 'three'
        return res
